import _ from 'lodash';
import dayjs from 'dayjs';
import BigNumber from 'bignumber.js';
import type { FormInstance } from 'antd/lib/form';
import type { RcFile } from 'antd/lib/upload/interface';

import {
  COMMON_SELECT_FIELDSNAMES,
  DATE_TIME_FORMAT,
  VALIDATE_COMMON_PERCENT_NUMBER
} from '@/utils/config';
import { PictureSuffixList, UploadFileSuffix } from '@/enumTypes/upload';

import word from '@assets/images/files/word.svg';
import excel from '@assets/images/files/excel.svg';
import txt from '@assets/images/files/txt.svg';
import pdf from '@assets/images/files/pdf.svg';
import ppt from '@assets/images/files/ppt.svg';
import rar from '@assets/images/files/rar.svg';
import zip from '@assets/images/files/zip.svg';
import otherFiles from '@assets/images/files/otherFiles.svg';

/**
 * 两个字符串是否存在 左开始连续包含关系
 * @param {string} includedStr 被包含字符串
 * @param {string} containStr 包含字符串(长度 >= includedStr)
 * @returns { boolean }
 */
export const leftStartContactContains = (
  includedStr = '',
  containStr: string | number | boolean = ''
): boolean => {
  let flag = false;

  if (
    typeof includedStr === 'string' &&
    typeof containStr === 'string' &&
    containStr.length >= includedStr.length
  ) {
    // 截取包含字符串左部分
    const partContainStr = containStr.slice(0, includedStr.length);

    // 截取部分全等则 存在左开始连续包含关系
    if (partContainStr === includedStr) {
      flag = true;
    }
  }

  return flag;
};

/**
 * 根据指定的键值-深度遍历寻找所在父节点集合
 * @param {any[]} dataArray 遍历数组
 * @param {string | number | boolean} keyValue 指定字段value
 * @param {string} keyName 指定字段key(默认key)
 * @returns {{ targetArray: any[], flag: boolean }}
 */
export const searchParentNode = (
  dataArray: any[],
  keyValue: string | number | boolean,
  keyName = 'key'
): { targetArray: any[]; flag: boolean } => {
  // 目标数组
  const targetArray = [];
  // 清除(结束)标识
  let flag = false;

  // 循环遍历
  for (const dataItem of dataArray) {
    // 如果找到对应的值(或存在左连续包含)
    if (dataItem[keyName] === keyValue || leftStartContactContains(dataItem[keyName], keyValue)) {
      // 包含子节点则删除子节点
      if (dataItem.children) {
        const cloneDataItem = { ...dataItem };
        delete cloneDataItem.children;
        targetArray.push(cloneDataItem);
      } else {
        targetArray.push(dataItem);
      }
      // 更新标识
      flag = true;
    } else {
      // 记录当前节点
      targetArray.push(dataItem);
      if (dataItem.children) {
        // 对子节点进行深度遍历
        const children = searchParentNode(dataItem.children, keyValue, keyName);
        // 替换当前节点中的子节点
        targetArray[0].children = children.targetArray;
        // 更新标识
        flag = children.flag;
      }
    }
    // 如果一轮下来其子节点也没有找到就删除当前节点
    if (!flag) {
      targetArray.pop();
    } else {
      return { targetArray, flag };
    }
  }

  return { targetArray, flag };
};

/**
 * 根据指定的键-深度遍历寻找所有key的值的集合
 * @param {any[]} dataArray 遍历数组
 * @param {string} keyName 指定字段key(默认key)
 * @returns { any[] }
 */
export const searchValueByKey = (dataArray: any[], keyName = 'key'): any[] => {
  // 最终目标数组
  const targetArray = [];

  // 循环遍历
  for (const dataItem of dataArray) {
    if (dataItem[keyName]) {
      targetArray.push(dataItem[keyName]);
    }
    if (dataItem.children) {
      const children = searchValueByKey(dataItem.children, keyName);
      targetArray.push(...children);
    }
  }

  return targetArray;
};

/**
 * 根据指定的值-遍历寻找数组中其它属性的(唯一)值
 * @param {any} value 指定值
 * @param {any[]} list 遍历数组
 * @param {string} valueName 指定字段key(默认key)
 * @param {string} labelName 其它属性字段key(默认value)
 * @returns { string | undefined }
 */
export const getPropValueByValue = (
  value: string | number | boolean,
  list: any[],
  valueName: string = COMMON_SELECT_FIELDSNAMES.value,
  labelName: string = COMMON_SELECT_FIELDSNAMES.label
): string | number | boolean | undefined => {
  if (!(_.isNumber(value) || _.isBoolean(value)) && (!value || _.isEmpty(list))) return value;

  // 包含value的数组
  const valueList = list.filter((item: any) => value === item[valueName]);

  if (_.isEmpty(valueList)) return value;

  return valueList[0][labelName];
};

/**
 * 根据指定的格式处理参数
 * @param {any} params 参数
 * @param {any} formats 指定格式
 * @returns { any }
 */
export const formatParams = (params: any, formats?: any): any => {
  const _params = { ...params };
  const _formats = { ...formats };

  // 参数不存在(为空)
  if (_.isEmpty(_params) || !formats) return params;

  const formatKeys = Object.keys(_formats);
  // 遍历参数转化格式
  for (const param in _params) {
    if (!_.isNil(_params[param])) {
      if (dayjs.isDayjs(_params[param])) {
        const format = formatKeys.includes(param) ? _formats[param] : DATE_TIME_FORMAT;
        _params[param] = dayjs(_params[param]).format(format);
      } else if (_.isArray(_params[param])) {
        const format = formatKeys.includes(param) ? _formats[param] : DATE_TIME_FORMAT;
        _params[`${param}From`] = dayjs.isDayjs(_params[param][0])
          ? dayjs(_params[param][0]).format(format)
          : _params[param][0]; // 开始
        _params[`${param}To`] = dayjs.isDayjs(_params[param][1])
          ? dayjs(_params[param][1]).format(format)
          : _params[param][1]; // 结束
        delete _params[param];
      }
    }
  }

  return _params;
};

/**
 * 根据指定的form-获取表单的值
 * @param {FormInstance} form 表单
 * @param {boolean} validate 是否校验
 * @param {any} formats 参数转化指定格式
 * @returns { any }
 */
export const getFormValues = async (
  form: FormInstance,
  validate = true,
  formats?: any
): Promise<any> => {
  if (validate) {
    const fields = await form?.validateFields();
    if (fields) return formatParams(fields, formats);
  } else {
    const fields = await form?.getFieldsValue();
    return formatParams(fields, formats);
  }
};

/**
 * 转换数字
 * 主要是为了百分比字符串转换成数字
 * @param {number | string} value 需转换值
 * @returns { number }
 */
export const toNumber = (value: number | string): number => {
  if (_.isNumber(value)) return value;

  if (_.isString(value)) {
    if (!_.isNaN(Number(value))) {
      return Number(value);
    } else if (VALIDATE_COMMON_PERCENT_NUMBER.pattern.test(value)) {
      // 删除最后一位的百分比
      const _value = value.substring(0, value.length - 1);
      return new BigNumber(Number(_value)).div(100).toNumber();
    } else {
      return NaN;
    }
  }

  return NaN;
};

/**
 * 文件对象(blob对象)转换base64数据字符串
 * @param {RcFile} file 文件对象
 * @returns { Promise<string> }
 */
export const fileToBase64String = (file: RcFile | Blob): Promise<string> =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result as string);
    reader.onerror = error => reject(error);
  });

/**
 * base64数据字符串转换文件对象
 * @param {string} base64String base64数据字符串
 * @param {FilePropertyBag & { filename: string }} options 配置项
 * @returns { File }
 */
export const base64StringToFile = (
  base64String = '',
  options: FilePropertyBag & { filename: string }
): File | undefined => {
  if (base64String) {
    // 获取base64编码
    const [_type, _data] = base64String.split(',');
    // 文件的mime类型
    const matchArr = _type.match(/:(.*?);/);
    const type = matchArr ? matchArr[1] : undefined;
    // 将base64编码转换成字符串
    const dataString = atob(_data);
    // 字符串长度
    let size = dataString.length;
    // 创建初始化0，包含size个元素的无符号整型数组
    const u8arr = new Uint8Array(size);
    while (size--) {
      u8arr[size] = dataString.charCodeAt(size);
    }
    // 获取文件名
    const { filename } = options;
    return new File([u8arr], filename, { type });
  }
};

/**
 * 下载文件
 *  @param {string} file 文件对象转成的base64字符串
 *  @param {string} name 文件名
 */
export const downloadFile = (file: string, name: string) => {
  const aLink = document.createElement('a');
  document.body.appendChild(aLink);
  aLink.style.display = 'none';
  aLink.href = file;
  const fileName = name;
  aLink.download = fileName;
  aLink.click();
  document.body.removeChild(aLink);
};

/**
 * 文件大小转换(由B->B|KB|MB|GB)
 *  @param {number} size 文件字节大小(单位B)
 *  @param {string} 带单位的文件大小
 */
export const bytesToSize = (size = 0) => {
  let fullSize = '0B';

  if (size > 0) {
    if (size < 1024) {
      fullSize = `${size}B`;
    } else if (size >= 1024 && size < 1024 ** 2) {
      fullSize = new BigNumber(size).div(1024).dp(2) + 'KB';
    } else if (size >= 1024 ** 2 && size < 1024 ** 3) {
      fullSize = new BigNumber(size).div(1024 ** 2).dp(2) + 'MB';
    } else {
      fullSize = new BigNumber(size).div(1024 ** 3).dp(2) + 'GB';
    }
  }

  return fullSize;
};

/**
 * 根据文件后缀名获取预览图标
 *  @param {string} suffix 文件后缀名
 *  @param {string} defaultUrl 文件默认预览Url
 *  @param {string} 实际预览iconUrl
 */
export const getPreviewIconUrl = (suffix: UploadFileSuffix, defaultUrl = '') => {
  // 图片类型则返回实际图片base64数据
  if (PictureSuffixList.includes(suffix)) return defaultUrl;

  switch (suffix) {
    case UploadFileSuffix.DOC:
    case UploadFileSuffix.DOCX:
      return word;
    case UploadFileSuffix.XLS:
    case UploadFileSuffix.XLSX:
      return excel;
    case UploadFileSuffix.TXT:
      return txt;
    case UploadFileSuffix.PDF:
      return pdf;
    case UploadFileSuffix.PPT:
    case UploadFileSuffix.PPTX:
      return ppt;
    case UploadFileSuffix.RAR:
      return rar;
    case UploadFileSuffix.ZIP:
      return zip;
    default:
      return otherFiles;
  }
};
